﻿import socket
from multiprocessing import Process
from struct import pack, unpack, calcsize
from os.path import isfile, isdir, normpath, dirname, getmtime, getsize
from os import getcwd, chdir, makedirs, remove, listdir, mkdir, rmdir, stat, rename
from time import ctime
import os
import rsa
import mysql.connector
from cryptography.fernet import Fernet
import hashlib
import logging

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('ftp_server.log'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

intSize = calcsize('i')
bufferSize = 40960  # 40KB缓冲区大小

# 添加数据库配置
DB_CONFIG = {
    'user': 'root',
    'password': '123456',
    'host': 'localhost',
    'database': '3ftp_db'
}


class MySQLDatabase:
    def __init__(self, config):
        self.config = config
        self.connection = None
        self.cursor = None

    def connect(self):
        try:
            self.connection = mysql.connector.connect(**self.config)
            self.cursor = self.connection.cursor()
            return True
        except mysql.connector.Error as err:
            logger.error(f"数据库连接错误: {err}")
            return False

    def close(self):
        if self.cursor:
            self.cursor.close()
        if self.connection:
            self.connection.close()

    def get_user(self, username):
        try:
            query = "SELECT password_hash, salt, home_dir, public_key FROM users WHERE username = %s"
            self.cursor.execute(query, (username,))
            result = self.cursor.fetchone()
            return result
        except mysql.connector.Error as err:
            logger.error(f"查询用户错误: {err}")
            return None

    def update_user_public_key(self, username, public_key):
        """更新用户的公钥"""
        try:
            query = "UPDATE users SET public_key = %s WHERE username = %s"
            self.cursor.execute(query, (public_key, username))
            self.connection.commit()
            logger.info(f"用户 {username} 的公钥已更新")
            return True
        except mysql.connector.Error as err:
            logger.error(f"更新用户公钥错误: {err}")
            return False

    def check_public_key(self, username):
        """检查用户是否有公钥"""
        try:
            query = "SELECT public_key FROM users WHERE username = %s"
            self.cursor.execute(query, (username,))
            result = self.cursor.fetchone()
            if result and result[0]:
                return True
            return False
        except mysql.connector.Error as err:
            logger.error(f"检查用户公钥错误: {err}")
            return False

    def add_user(self, username, password_hash, salt, home_dir, public_key):
        try:
            # 先检查用户是否存在
            self.cursor.execute("SELECT 1 FROM users WHERE username = %s", (username,))
            if self.cursor.fetchone():
                return False
                
            query = """INSERT INTO users (username, password_hash, salt, home_dir, public_key) 
                       VALUES (%s, %s, %s, %s, %s)"""
            self.cursor.execute(query, (username, password_hash, salt, home_dir, public_key))
            self.connection.commit()
            
            # 创建用户主目录
            if not os.path.exists(home_dir):
                os.makedirs(home_dir, exist_ok=True)
                
            return True
        except mysql.connector.Error as err:
            logger.error(f"添加用户错误: {err}")
            return False

    def __del__(self):
        self.close()


def server(conn, addr):
    logger.info(f'新客户端连接：{str(addr)}')

    # 创建数据库连接
    db = MySQLDatabase(DB_CONFIG)
    if not db.connect():
        conn.close()
        logger.error("数据库连接失败，关闭客户端连接")
        return

    try:
        # 接收客户端发送的用户名（用于获取公钥）
        length = unpack('i', conn.recv(intSize))[0]
        username = conn.recv(length).decode()
        logger.info(f"用户 {username} 尝试登录")

        # 从数据库获取用户信息
        user_info = db.get_user(username)
        if not user_info:
            logger.warning(f"用户 {username} 不存在")
            conn.sendall(pack('i', 5) + b'error')
            conn.close()
            return

        stored_hash, salt, home_dir, pub_key_str = user_info
        
        # 生成会话对称密钥
        session_key = Fernet.generate_key()
        cipher = Fernet(session_key)
        logger.info(f"为用户 {username} 生成会话密钥")

        try:
            # 检查是否有公钥
            if pub_key_str and pub_key_str.strip():
                # 加载公钥
                public_key = rsa.PublicKey.load_pkcs1(pub_key_str.encode())
                
                # 使用用户公钥加密会话密钥
                encrypted_key = rsa.encrypt(session_key, public_key)
                conn.sendall(pack('i', len(encrypted_key)) + encrypted_key)
                logger.info(f"使用用户公钥加密会话密钥并发送")
            else:
                # 用户没有公钥，告知客户端需要首次设置公钥
                logger.warning(f"用户 {username} 没有公钥，将请求设置公钥")
                
                # 发送特殊标识，指示需要设置公钥
                conn.sendall(pack('i', 11) + b'need_pubkey')
                
                # 等待接收公钥
                try:
                    length = unpack('i', conn.recv(intSize))[0]
                    public_key_data = conn.recv(length)
                    
                    # 将公钥保存到数据库
                    if db.update_user_public_key(username, public_key_data.decode()):
                        logger.info(f"成功接收并保存用户 {username} 的公钥")
                        
                        # 加载新保存的公钥
                        public_key = rsa.PublicKey.load_pkcs1(public_key_data)
                        
                        # 使用用户公钥加密会话密钥
                        encrypted_key = rsa.encrypt(session_key, public_key)
                        conn.sendall(pack('i', len(encrypted_key)) + encrypted_key)
                        logger.info(f"使用新公钥加密会话密钥并发送")
                    else:
                        logger.error(f"保存用户 {username} 的公钥失败")
                        conn.sendall(pack('i', 5) + b'error')
                        conn.close()
                        return
                except Exception as e:
                    logger.error(f"接收公钥错误: {e}")
                    conn.sendall(pack('i', 5) + b'error')
                    conn.close()
                    return
        except Exception as e:
            logger.error(f"密钥交换错误: {e}")
            conn.sendall(pack('i', 5) + b'error')
            conn.close()
            return

        # 接收加密的用户凭证
        try:
            length = unpack('i', conn.recv(intSize))[0]
            encrypted_cred = conn.recv(length)

            # 解密凭证
            cred = cipher.decrypt(encrypted_cred).decode()
            userId, userPwd = cred.split(',')
            logger.info(f"成功接收和解密用户凭证")
        except Exception as e:
            logger.error(f"凭证解密错误: {e}")
            # 可能是密钥不匹配导致的解密错误
            try:
                # 告知客户端这是密钥不匹配问题
                msg = b'key_mismatch'
                # 使用之前生成的session_key创建新的cipher，因为可能是密钥问题
                temp_cipher = Fernet(session_key)
                encrypted_msg = temp_cipher.encrypt(msg)
                conn.sendall(pack('i', len(encrypted_msg)) + encrypted_msg)
                logger.warning(f"用户 {username} 的密钥不匹配，已通知客户端")
            except:
                pass
            conn.close()
            return

        # 验证用户
        if userId != username:
            logger.warning(f"用户名不匹配: {userId} vs {username}")
            msg = b'error'
            encrypted_msg = cipher.encrypt(msg)
            conn.sendall(pack('i', len(encrypted_msg)) + encrypted_msg)
            conn.close()
            return

        # 计算输入密码的哈希
        input_hash = hashlib.sha256((userPwd + salt).encode()).hexdigest()

        if input_hash == stored_hash:
            logger.info(f"用户 {username} 验证成功")
            msg = b'ok'
            # 发送加密的响应
            encrypted_msg = cipher.encrypt(msg)
            conn.sendall(pack('i', len(encrypted_msg)) + encrypted_msg)
        else:
            logger.warning(f"用户 {username} 密码错误")
            msg = b'error'
            encrypted_msg = cipher.encrypt(msg)
            conn.sendall(pack('i', len(encrypted_msg)) + encrypted_msg)
            conn.close()
            return

        # 用户主目录不存在，为其创建一个
        if not isdir(home_dir):
            try:
                makedirs(home_dir, exist_ok=True)
                logger.info(f"为用户 {username} 创建主目录 {home_dir}")
            except Exception as e:
                logger.error(f"创建用户目录失败: {e}")

        # 进入当前用户主目录
        try:
            chdir(home_dir)
            logger.info(f"切换到用户 {username} 的主目录 {home_dir}")
        except Exception as e:
            logger.error(f"切换目录失败: {e}")
            conn.close()
            return

        # 存储聊天记录的缓冲区
        chat_history = []
        
        while True:
            try:
                # 接收加密命令
                length = unpack('i', conn.recv(intSize))[0]
                encrypted_command = conn.recv(length)

                # 解密命令
                command = cipher.decrypt(encrypted_command).decode().lower()
                logger.info(f"接收到来自客户端 {addr} 的命令: {command}")
            except Exception as e:
                logger.error(f"命令接收/解密失败: {e}")
                break

            # 客户端退出
            if command in ('quit', 'q'):
                logger.info(f"客户端 {addr} 请求退出")
                break

            # 聊天功能 - 新增
            elif command.startswith('chat '):
                try:
                    message = command[5:]  # 提取消息内容
                    chat_history.append(f"{username}: {message}")
                    
                    # 保存最近的20条消息
                    if len(chat_history) > 20:
                        chat_history = chat_history[-20:]
                    
                    # 发送确认和聊天历史
                    chat_response = "\n".join(chat_history).encode()
                    encrypted_response = cipher.encrypt(chat_response)
                    conn.sendall(pack('i', len(encrypted_response)) + encrypted_response)
                    logger.info(f"处理聊天消息: {message[:30]}...")
                except Exception as e:
                    logger.error(f"处理聊天消息错误: {e}")

            # 查看当前文件夹的文件列表
            elif command in ('list', 'ls', 'dir'):
                try:
                    files = str(listdir(getcwd())).encode()
                    encrypted_files = cipher.encrypt(files)
                    conn.sendall(pack('I', len(encrypted_files)) + encrypted_files)
                    logger.info(f"发送目录列表: {getcwd()}")
                except Exception as e:
                    logger.error(f"文件列表错误: {e}")

            # 切换至上一级目录
            elif ''.join(command.split()) == 'cd..':
                try:
                    cwd = getcwd()
                    newCwd = dirname(cwd)
                    # 考虑根目录的情况
                    if newCwd[-1] == ':':
                        newCwd += '\\'
                    # 限定用户主目录
                    if newCwd.lower().startswith(home_dir.lower()):
                        chdir(newCwd)
                        msg = b'ok'
                        logger.info(f"切换到上级目录: {newCwd}")
                    else:
                        msg = b'error'
                        logger.warning(f"拒绝切换目录: 超出用户主目录")
                    encrypted_msg = cipher.encrypt(msg)
                    conn.sendall(pack('i', len(encrypted_msg)) + encrypted_msg)
                except Exception as e:
                    logger.error(f"目录切换错误: {e}")

            # 查看当前目录
            elif command in ('cwd', 'cd', 'pwd'):
                try:
                    msg = str(getcwd()).encode()
                    encrypted_msg = cipher.encrypt(msg)
                    conn.sendall(pack('i', len(encrypted_msg)) + encrypted_msg)
                    logger.info(f"发送当前目录: {getcwd()}")
                except Exception as e:
                    logger.error(f"获取当前目录错误: {e}")

            # 切换到指定目录
            elif command.startswith('cd '):
                try:
                    # 指定最大分隔次数，考虑目标文件夹带有空格的情况
                    # 只允许使用相对路径进行跳转，不允许有类似'C:\'类似的路径
                    command_parts = command.split(maxsplit=1)
                    target_dir = command_parts[1]
                    
                    # 构建绝对路径
                    if target_dir.startswith('/'):
                        abs_path = os.path.normpath(os.path.join(home_dir, target_dir[1:]))
                    else:
                        abs_path = os.path.normpath(os.path.join(getcwd(), target_dir))
                    
                    # 检查目标路径是否在用户主目录下
                    if isdir(target_dir) and abs_path.startswith(home_dir):
                        chdir(target_dir)
                        msg = b'ok'
                        logger.info(f"切换到目录: {target_dir}")
                    else:
                        msg = b'error'
                        logger.warning(f"拒绝切换到目录: {target_dir}")
                    
                    encrypted_msg = cipher.encrypt(msg)
                    conn.sendall(pack('i', len(encrypted_msg)) + encrypted_msg)
                except Exception as e:
                    logger.error(f"目录切换错误: {e}")
                    encrypted_msg = cipher.encrypt(b'error')
                    conn.sendall(pack('i', len(encrypted_msg)) + encrypted_msg)

            # 下载文件
            elif command.startswith('get '):
                try:
                    # 分隔命令和要下载的文件名
                    command_parts = command.split(maxsplit=1)
                    filename = command_parts[1]
                    
                    # 检查文件是否存在
                    if len(command_parts) == 2 and isfile(filename):
                        # 确认文件存在
                        encrypted_ok = cipher.encrypt(b'ok')
                        conn.sendall(pack('i', len(encrypted_ok)) + encrypted_ok)
                        logger.info(f"准备发送文件: {filename}")

                        # 获取文件大小
                        file_size = getsize(filename)
                        size_bytes = pack('I', file_size)
                        encrypted_size = cipher.encrypt(size_bytes)
                        conn.sendall(pack('I', len(encrypted_size)) + encrypted_size)
                        
                        # 分块读取和发送文件
                        chunk_size = 1024 * 1024  # 1MB
                        with open(filename, 'rb') as fp:
                            sent = 0
                            while sent < file_size:
                                chunk = fp.read(min(chunk_size, file_size - sent))
                                if not chunk:
                                    break
                                    
                                encrypted_chunk = cipher.encrypt(chunk)
                                conn.sendall(pack('I', len(encrypted_chunk)) + encrypted_chunk)
                                sent += len(chunk)
                                
                        logger.info(f"文件 {filename} 发送完成, 大小: {file_size} 字节")
                    else:
                        encrypted_no = cipher.encrypt(b'no')
                        conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                        logger.warning(f"文件不存在: {filename}")
                except Exception as e:
                    logger.error(f"文件下载错误: {e}")

            # 上传文件
            elif command.startswith('put '):
                try:
                    # 获取文件名
                    fn = command.split(maxsplit=1)[1]
                    # 如果文件已存在或文件名、类型不符合要求，拒绝上传
                    if (isfile(fn) or fn.endswith(('.exe', '.com')) 
                            or ('\\' in normpath(fn))):
                        encrypted_no = cipher.encrypt(b'no')
                        conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                        logger.warning(f"拒绝上传文件: {fn}")
                    else:
                        # 文件不存在且符合要求，可以接收该文件
                        encrypted_ok = cipher.encrypt(b'ok')
                        conn.sendall(pack('i', len(encrypted_ok)) + encrypted_ok)
                        logger.info(f"准备接收文件: {fn}")

                        # 接收加密文件大小
                        length = unpack('i', conn.recv(intSize))[0]
                        encrypted_size = conn.recv(length)
                        size = unpack('I', cipher.decrypt(encrypted_size))[0]
                        
                        # 分块接收和写入文件
                        with open(fn, 'wb') as fp:
                            received = 0
                            while received < size:
                                # 接收加密数据块
                                length = unpack('i', conn.recv(intSize))[0]
                                encrypted_chunk = conn.recv(length)
                                
                                # 解密数据块
                                chunk = cipher.decrypt(encrypted_chunk)
                                received += len(chunk)
                                fp.write(chunk)
                        
                        # 确认接收成功
                        encrypted_ok = cipher.encrypt(b'ok')
                        conn.sendall(pack('i', len(encrypted_ok)) + encrypted_ok)
                        logger.info(f"文件 {fn} 接收完成, 大小: {size} 字节")
                except Exception as e:
                    logger.error(f"文件上传错误: {e}")
                    try:
                        encrypted_no = cipher.encrypt(b'error')
                        conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                    except:
                        pass

            # 删除服务器上的文件
            elif command.startswith('del '):
                try:
                    fn = command.split(maxsplit=1)[1]
                    if '\\' not in normpath(fn) and isfile(fn):
                        try:
                            remove(fn)
                            encrypted_ok = cipher.encrypt(b'ok')
                            conn.sendall(pack('i', len(encrypted_ok)) + encrypted_ok)
                            logger.info(f"删除文件: {fn}")
                        except Exception as e:
                            encrypted_no = cipher.encrypt(b'no')
                            conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                            logger.error(f"删除文件失败: {fn}, 错误: {e}")
                    else:
                        encrypted_no = cipher.encrypt(b'no')
                        conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                        logger.warning(f"拒绝删除: 无效文件路径 {fn}")
                except Exception as e:
                    logger.error(f"文件删除错误: {e}")

            # 创建子文件夹
            elif command.startswith('md ') or command.startswith('mkdir '):
                try:
                    if command.startswith('md '):
                        subDir = command.split(maxsplit=1)[1]
                    else:
                        subDir = command.split(maxsplit=1)[1]
                        
                    if '\\' not in normpath(subDir) and not isdir(subDir):
                        try:
                            mkdir(subDir)
                            encrypted_ok = cipher.encrypt(b'ok')
                            conn.sendall(pack('i', len(encrypted_ok)) + encrypted_ok)
                            logger.info(f"创建目录: {subDir}")
                        except Exception as e:
                            encrypted_no = cipher.encrypt(b'no')
                            conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                            logger.error(f"创建目录失败: {subDir}, 错误: {e}")
                    else:
                        encrypted_no = cipher.encrypt(b'no')
                        conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                        logger.warning(f"拒绝创建目录: 无效路径或已存在 {subDir}")
                except Exception as e:
                    logger.error(f"目录创建错误: {e}")

            # 添加用户
            elif command.startswith('add '):
                try:
                    parts = command.split(maxsplit=4)
                    if len(parts) == 5:
                        _, new_user, password, new_home_dir, pub_key_str = parts
                        
                        # 检查用户是否有管理员权限 (只允许特定用户添加新用户)
                        if username in ['zhangsan', 'admin']:  # 假设这些是管理员
                            # 生成随机盐值
                            salt = os.urandom(16).hex()
                            # 计算密码哈希
                            password_hash = hashlib.sha256((password + salt).encode()).hexdigest()
                            
                            if db.add_user(new_user, password_hash, salt, new_home_dir, pub_key_str):
                                encrypted_ok = cipher.encrypt(b'ok')
                                logger.info(f"添加新用户成功: {new_user}")
                            else:
                                encrypted_ok = cipher.encrypt(b'exists')
                                logger.warning(f"添加用户失败: {new_user} 已存在")
                        else:
                            encrypted_ok = cipher.encrypt(b'error')
                            logger.warning(f"用户 {username} 没有添加新用户的权限")
                    else:
                        encrypted_ok = cipher.encrypt(b'error')
                        logger.warning(f"添加用户命令格式错误")
                        
                    conn.sendall(pack('i', len(encrypted_ok)) + encrypted_ok)
                except Exception as e:
                    logger.error(f"添加用户错误: {e}")
                    try:
                        encrypted_err = cipher.encrypt(b'error')
                        conn.sendall(pack('i', len(encrypted_err)) + encrypted_err)
                    except:
                        pass

            # 更新用户公钥
            elif command == 'update_key':
                try:
                    # 发送准备接收公钥的信号
                    encrypted_ready = cipher.encrypt("ready_for_key".encode())
                    conn.sendall(pack('i', len(encrypted_ready)) + encrypted_ready)
                    logger.info(f"准备接收用户 {username} 的新公钥")
                    
                    # 接收加密的公钥
                    length = unpack('i', conn.recv(intSize))[0]
                    encrypted_key = conn.recv(length)
                    public_key_data = cipher.decrypt(encrypted_key)
                    
                    # 更新数据库中的公钥
                    if db.update_user_public_key(username, public_key_data.decode()):
                        result = "success"
                        logger.info(f"用户 {username} 的公钥已更新")
                    else:
                        result = "failed"
                        logger.error(f"更新用户 {username} 的公钥失败")
                    
                    # 发送更新结果
                    encrypted_result = cipher.encrypt(result.encode())
                    conn.sendall(pack('i', len(encrypted_result)) + encrypted_result)
                except Exception as e:
                    logger.error(f"公钥更新错误: {e}")
                    try:
                        encrypted_error = cipher.encrypt("failed".encode())
                        conn.sendall(pack('i', len(encrypted_error)) + encrypted_error)
                    except:
                        pass
            
            # 删除目录
            elif command.startswith('rd ') or command.startswith('rmdir '):
                try:
                    if command.startswith('rd '):
                        dirname = command.split(maxsplit=1)[1]
                    else:
                        dirname = command.split(maxsplit=1)[1]
                        
                    if '\\' not in normpath(dirname) and isdir(dirname):
                        try:
                            rmdir(dirname)
                            encrypted_ok = cipher.encrypt(b'ok')
                            conn.sendall(pack('i', len(encrypted_ok)) + encrypted_ok)
                            logger.info(f"删除目录: {dirname}")
                        except Exception as e:
                            encrypted_no = cipher.encrypt(b'no')
                            conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                            logger.error(f"删除目录失败: {dirname}, 错误: {e}")
                    else:
                        encrypted_no = cipher.encrypt(b'no')
                        conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                        logger.warning(f"拒绝删除目录: 无效路径或非空目录 {dirname}")
                except Exception as e:
                    logger.error(f"目录删除错误: {e}")

            # 重命名文件
            elif command.startswith('rename ') or command.startswith('ren '):
                try:
                    parts = command.split(maxsplit=2)
                    if len(parts) == 3:
                        old_name = parts[1]
                        new_name = parts[2]
                        
                        if '\\' not in normpath(old_name) and '\\' not in normpath(new_name) and isfile(old_name):
                            try:
                                rename(old_name, new_name)
                                encrypted_ok = cipher.encrypt(b'ok')
                                conn.sendall(pack('i', len(encrypted_ok)) + encrypted_ok)
                                logger.info(f"重命名文件: {old_name} -> {new_name}")
                            except Exception as e:
                                encrypted_no = cipher.encrypt(b'no')
                                conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                                logger.error(f"重命名失败: {e}")
                        else:
                            encrypted_no = cipher.encrypt(b'no')
                            conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                            logger.warning(f"拒绝重命名: 无效路径 {old_name} -> {new_name}")
                    else:
                        encrypted_no = cipher.encrypt(b'no')
                        conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                        logger.warning(f"重命名命令格式错误")
                except Exception as e:
                    logger.error(f"重命名错误: {e}")

            # 查看文件属性
            elif command.startswith('attr ') or command.startswith('attrib '):
                try:
                    filename = command.split(maxsplit=1)[1]
                    if '\\' not in normpath(filename) and isfile(filename):
                        try:
                            stat_info = stat(filename)
                            attrs = {
                                'size': getsize(filename),
                                'modified': ctime(getmtime(filename)),
                                'mode': stat_info.st_mode
                            }
                            attr_str = str(attrs).encode()
                            encrypted_attrs = cipher.encrypt(attr_str)
                            conn.sendall(pack('i', len(encrypted_attrs)) + encrypted_attrs)
                            logger.info(f"获取文件属性: {filename}")
                        except Exception as e:
                            encrypted_no = cipher.encrypt(b'no')
                            conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                            logger.error(f"获取属性失败: {e}")
                    else:
                        encrypted_no = cipher.encrypt(b'no')
                        conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                        logger.warning(f"拒绝获取属性: 无效文件 {filename}")
                except Exception as e:
                    logger.error(f"属性获取错误: {e}")

            # 判断路径类型
            elif command.startswith('di '):
                try:
                    path = command.split(maxsplit=1)[1]
                    if isfile(path):
                        result = 'file'
                    elif isdir(path):
                        result = 'directory'
                    else:
                        result = 'not_found'
                    encrypted_result = cipher.encrypt(result.encode())
                    conn.sendall(pack('i', len(encrypted_result)) + encrypted_result)
                    logger.info(f"判断路径类型: {path} -> {result}")
                except Exception as e:
                    logger.error(f"路径判断错误: {e}")

            # 创建空文件
            elif command.startswith('create '):
                try:
                    filename = command.split(maxsplit=1)[1]
                    if '\\' not in normpath(filename) and not isfile(filename):
                        try:
                            with open(filename, 'w') as f:
                                pass
                            encrypted_ok = cipher.encrypt(b'ok')
                            conn.sendall(pack('i', len(encrypted_ok)) + encrypted_ok)
                            logger.info(f"创建空文件: {filename}")
                        except Exception as e:
                            encrypted_no = cipher.encrypt(b'no')
                            conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                            logger.error(f"创建文件失败: {e}")
                    else:
                        encrypted_no = cipher.encrypt(b'no')
                        conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                        logger.warning(f"拒绝创建文件: 无效路径或已存在 {filename}")
                except Exception as e:
                    logger.error(f"创建文件错误: {e}")

            # 显示目录树
            elif command == 'tree':
                try:
                    def build_tree(path, prefix=''):
                        tree = []
                        contents = listdir(path)
                        for i, item in enumerate(contents):
                            is_last = i == len(contents) - 1
                            new_prefix = prefix + ('    ' if is_last else '│   ')
                            tree.append(prefix + ('└── ' if is_last else '├── ') + item)
                            if isdir(os.path.join(path, item)):
                                tree.extend(build_tree(os.path.join(path, item), new_prefix))
                        return tree
                    
                    tree = build_tree(getcwd())
                    tree_str = '\n'.join(tree).encode()
                    encrypted_tree = cipher.encrypt(tree_str)
                    conn.sendall(pack('i', len(encrypted_tree)) + encrypted_tree)
                    logger.info(f"生成目录树: {getcwd()}")
                except Exception as e:
                    logger.error(f"目录树生成错误: {e}")

            # 保存当前目录
            elif command == 'pushd':
                try:
                    saved_dir = getcwd()
                    encrypted_dir = cipher.encrypt(saved_dir.encode())
                    conn.sendall(pack('i', len(encrypted_dir)) + encrypted_dir)
                    logger.info(f"保存当前目录: {saved_dir}")
                except Exception as e:
                    logger.error(f"保存目录错误: {e}")

            # 恢复保存的目录
            elif command == 'popd':
                try:
                    length = unpack('i', conn.recv(intSize))[0]
                    encrypted_dir = conn.recv(length)
                    saved_dir = cipher.decrypt(encrypted_dir).decode()
                    if isdir(saved_dir):
                        chdir(saved_dir)
                        encrypted_ok = cipher.encrypt(b'ok')
                        conn.sendall(pack('i', len(encrypted_ok)) + encrypted_ok)
                        logger.info(f"恢复目录: {saved_dir}")
                    else:
                        encrypted_no = cipher.encrypt(b'no')
                        conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                        logger.warning(f"目录不存在: {saved_dir}")
                except Exception as e:
                    logger.error(f"恢复目录错误: {e}")

            # 复制文件
            elif command.startswith('copy '):
                try:
                    parts = command.split(maxsplit=2)
                    if len(parts) == 3:
                        src = parts[1]
                        dst = parts[2]
                        
                        if '\\' not in normpath(src) and '\\' not in normpath(dst) and isfile(src):
                            try:
                                with open(src, 'rb') as f_src, open(dst, 'wb') as f_dst:
                                    f_dst.write(f_src.read())
                                encrypted_ok = cipher.encrypt(b'ok')
                                conn.sendall(pack('i', len(encrypted_ok)) + encrypted_ok)
                                logger.info(f"复制文件: {src} -> {dst}")
                            except Exception as e:
                                encrypted_no = cipher.encrypt(b'no')
                                conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                                logger.error(f"复制失败: {e}")
                        else:
                            encrypted_no = cipher.encrypt(b'no')
                            conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                            logger.warning(f"拒绝复制: 无效路径 {src} -> {dst}")
                    else:
                        encrypted_no = cipher.encrypt(b'no')
                        conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                        logger.warning(f"复制命令格式错误")
                except Exception as e:
                    logger.error(f"复制错误: {e}")

            # 递归复制目录
            elif command.startswith('xcopy '):
                try:
                    parts = command.split(maxsplit=2)
                    if len(parts) == 3:
                        src = parts[1]
                        dst = parts[2]
                        
                        if '\\' not in normpath(src) and '\\' not in normpath(dst) and isdir(src):
                            try:
                                # 创建目标目录
                                if not isdir(dst):
                                    makedirs(dst)
                                
                                # 递归复制文件和子目录
                                for item in listdir(src):
                                    src_path = os.path.join(src, item)
                                    dst_path = os.path.join(dst, item)
                                    
                                    if isfile(src_path):
                                        with open(src_path, 'rb') as f_src, open(dst_path, 'wb') as f_dst:
                                            f_dst.write(f_src.read())
                                    elif isdir(src_path):
                                        # 递归处理子目录
                                        encrypted_recursive = cipher.encrypt(f"xcopy {src_path} {dst_path}".encode())
                                        conn.sendall(pack('i', len(encrypted_recursive)) + encrypted_recursive)
                                
                                encrypted_ok = cipher.encrypt(b'ok')
                                conn.sendall(pack('i', len(encrypted_ok)) + encrypted_ok)
                                logger.info(f"递归复制目录: {src} -> {dst}")
                            except Exception as e:
                                encrypted_no = cipher.encrypt(b'no')
                                conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                                logger.error(f"递归复制失败: {e}")
                        else:
                            encrypted_no = cipher.encrypt(b'no')
                            conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                            logger.warning(f"拒绝递归复制: 无效路径 {src} -> {dst}")
                    else:
                        encrypted_no = cipher.encrypt(b'no')
                        conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                        logger.warning(f"递归复制命令格式错误")
                except Exception as e:
                    logger.error(f"递归复制错误: {e}")

            # 移动文件
            elif command.startswith('move '):
                try:
                    parts = command.split(maxsplit=2)
                    if len(parts) == 3:
                        src = parts[1]
                        dst = parts[2]
                        
                        if '\\' not in normpath(src) and '\\' not in normpath(dst) and isfile(src):
                            try:
                                rename(src, dst)
                                encrypted_ok = cipher.encrypt(b'ok')
                                conn.sendall(pack('i', len(encrypted_ok)) + encrypted_ok)
                                logger.info(f"移动文件: {src} -> {dst}")
                            except Exception as e:
                                encrypted_no = cipher.encrypt(b'no')
                                conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                                logger.error(f"移动失败: {e}")
                        else:
                            encrypted_no = cipher.encrypt(b'no')
                            conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                            logger.warning(f"拒绝移动: 无效路径 {src} -> {dst}")
                    else:
                        encrypted_no = cipher.encrypt(b'no')
                        conn.sendall(pack('i', len(encrypted_no)) + encrypted_no)
                        logger.warning(f"移动命令格式错误")
                except Exception as e:
                    logger.error(f"移动错误: {e}")

            # 无效命令
            else:
                logger.warning(f"无效命令: {command}")
                try:
                    encrypted_err = cipher.encrypt(b'Invalid command. Use help or ? to see available commands.')
                    conn.sendall(pack('i', len(encrypted_err)) + encrypted_err)
                except:
                    pass

    except Exception as e:
        logger.error(f"处理客户端 {addr} 时发生错误: {e}")
    finally:
        conn.close()
        logger.info(f'客户端 {str(addr)} 关闭连接')


if __name__ == '__main__':
    try:
        # 要求输入MySQL root密码
        DB_CONFIG['password'] = input("请输入MySQL root密码: ")

        # 创建数据库对象
        db = MySQLDatabase(DB_CONFIG)
        if not db.connect():
            print("数据库连接失败，服务器退出")
            exit()

        # 创建Socket，监听本地端口，等待客户端连接
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.bind(('', 10800))
        sock.listen(5)
        print('服务器已启动，监听端口 10800...')
        logger.info('服务器已启动，监听端口 10800')

        while True:
            try:
                conn, addr = sock.accept()
                # 为每个客户端连接创建并启动一个进程
                Process(target=server, args=(conn, addr)).start()
            except Exception as e:
                logger.error(f"服务器主循环错误: {e}")
                break
                
    except KeyboardInterrupt:
        print("服务器关闭")
        logger.info("服务器关闭")
    except Exception as e:
        print(f"服务器错误: {e}")
        logger.error(f"服务器错误: {e}")
